package yxxy.c_019.Semaphore;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.ReentrantLock;

/**
 *  这里，抛出一个问题，上面的代码，不用 synchronized 实现，而使用 ReentrantLock 来实现，按理说会更好的，原因如下：
 *
 * 　　　　synchronized 是 jvm 层面的实现，ReentrantLock 是 jdk 层面的实现，synchronized 的缺点如下：
 *
 * 　　　　1）不能响应中断；
 *
 * 　　　　2）同一时刻不管是读还是写都只能有一个线程对共享资源操作，其他线程只能等待
 *
 * 　　　　3）锁的释放由虚拟机来完成，不用人工干预，不过此即使缺点也是优点，优点是不用担心会造成死锁，缺点是由可能获取到锁的线程阻塞之后其他线程会一直等待，性能不高。
 *
 * 　　而lock接口的提出就是为了完善synchronized的不完美的，首先lock是基于jdk层面实现的接口，和虚拟机层面不是一个概念；其次对于lock对象中的多个方法的调用，可以灵活控制对共享资源变量的操作，不管是读操作还是写操作
 *
 * 　　那么上面的代码如果使用 ReentrantLock 来实现，岂不是更好吗？好，修改 SemaphoreService
 */
@SuppressWarnings("all")
public class SemaphoreExample7 extends SemaphoreExample3{
    private static SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    private Semaphore semaphore = new Semaphore(2);// 同步关键类，构造方法传入的数字是多少，则同一个时刻，只运行多少个进程同时运行制定代码

    private ReentrantLock lock = new ReentrantLock();

    public void doSomething() {
        try {
            /**
             * 在 semaphore.acquire() 和 semaphore.release()之间的代码，同一时刻只允许制定个数的线程进入，
             * 因为semaphore的构造方法是1，则同一时刻只允许一个线程进入，其他线程只能等待。
             * */
            semaphore.acquire();

            lock.lock();
            doSomethingMain(); // 将主要处理部分封装成一个方法

            semaphore.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    private void doSomethingMain() throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + ":doSomething start-" + GetFormatTimeStr());
        Thread.sleep(2000);
        System.out.println(Thread.currentThread().getName() + ":doSomething end-" + GetFormatTimeStr());
    }

    public static void main(String[] args) {
        SemaphoreExample7 service = new SemaphoreExample7(); // 使用总 6 通路，每个线程占用2通路，尝试获取锁
        for (int i = 0; i < 10; i++) {
            MyThread t = new MyThread("thread" + (i + 1), service);
            t.start();
        }
    }
}
